#include <string.h>
#ifndef __Win32__
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/uio.h>
#include <unistd.h>
#include <netinet/tcp.h>
#include <fcntl.h>
#endif

#include <errno.h>
#include "OS.h"
#include "Socket.h"
#include "SocketUtils.h"
#include "OSThread.h"
#include "MyAssert.h"

#ifdef USE_NETLOG
#include <netlog.h>
#endif

#define INVALID_FD	(-1)

Socket::Socket()
:   fState(kBlockingSocketType),
//    fLocalAddrStrPtr(NULL),
//   fLocalDNSStrPtr(NULL),
    fPortStr(fPortBuffer, kPortBufSizeInBytes)
{
	fFileDesc = INVALID_FD;
    fLocalAddr.sin_addr.s_addr = 0;
    fLocalAddr.sin_port = 0;
    fDestAddr.sin_addr.s_addr = 0;
    fDestAddr.sin_port = 0;   
	m_bIsBlocking = FALSE;
}

Socket::~Socket()
{
	Close();
}

void Socket::Initialize()
{
#ifdef __Win32__
    // Start Win32 DLLs
    WORD wsVersion = MAKEWORD(1, 1);
    WSADATA wsData;
    (void)::WSAStartup(wsVersion, &wsData);
#else
#endif
}

void Socket::Uninitialize()
{
#ifdef __Win32__
	WSACleanup();
#else
#endif
}

OS_Error Socket::Open(int theType)
{
	Close();
	
	if(fFileDesc == INVALID_FD)
	{
		fFileDesc = ::socket(PF_INET, theType, 0);
		if (fFileDesc == kInvalidFileDesc)
		{
			Int32 ErrNo = (OS_Error)OS::GetErrno();
			return ErrNo;
		}
#ifdef __Win32__
		//set default send and receive buffer size
		SetSocketBufSize(32*1024);
		SetSocketRcvBufSize(32*1024);
#endif
	}      
    return OS_NoErr;
}

void Socket::Close()
{
	if(fFileDesc != INVALID_FD)
	{
#ifdef __Win32__
		::closesocket(fFileDesc);
#else
		//::shutdown(fFileDesc,SHUT_RDWR);
		::close(fFileDesc);
#endif

		fFileDesc = INVALID_FD;
	}
}

void Socket::ReuseAddr()
{
    int one = 1;
    int err = ::setsockopt(fFileDesc, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(int));
    Assert(err == 0);   
}

void Socket::NoDelay()
{
    int one = 1;
    int err = ::setsockopt(fFileDesc, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(int));
    Assert(err == 0);   
}

void Socket::KeepAlive()
{
    int one = 1;
    int err = ::setsockopt(fFileDesc, SOL_SOCKET, SO_KEEPALIVE, (char*)&one, sizeof(int));
    Assert(err == 0);   
}

void    Socket::SetSocketBufSize(UInt32 inNewSize)
{
    int bufSize = inNewSize;
    int err = ::setsockopt(fFileDesc, SOL_SOCKET, SO_SNDBUF, (char*)&bufSize, sizeof(int));
    Assert(err == 0);
}

OS_Error    Socket::SetSocketRcvBufSize(UInt32 inNewSize)
{
    int bufSize = inNewSize;
    int err = ::setsockopt(fFileDesc, SOL_SOCKET, SO_RCVBUF, (char*)&bufSize, sizeof(int));
    if (err == -1)
        return OS::GetErrno();
    return OS_NoErr;
}

OS_Error Socket::Bind(UInt32 addr, UInt16 port)
{
#ifndef USE_NETLOG
#if defined(__Win32__)
    // missing from some platform includes
    typedef int socklen_t;
#endif
#endif
    socklen_t len = sizeof(fLocalAddr);
    ::memset(&fLocalAddr, 0, sizeof(fLocalAddr));
    fLocalAddr.sin_family = AF_INET;
    fLocalAddr.sin_port = htons(port);
    fLocalAddr.sin_addr.s_addr = htonl(addr);

    int err = ::bind(fFileDesc, (sockaddr *)&fLocalAddr, sizeof(fLocalAddr));
    if (err == -1)
    {
        fLocalAddr.sin_port = 0;
        fLocalAddr.sin_addr.s_addr = 0;
        return (OS_Error)OS::GetErrno();
    }
    else 
		::getsockname(fFileDesc, (sockaddr *)&fLocalAddr, &len); // get the kernel to fill in unspecified values
   
	fState |= kBound;
    return OS_NoErr;
}

std::string Socket::GetLocalAddrStr()
{
	string strTmp;
	SocketUtils::ConvertAddrToString(fLocalAddr.sin_addr, &strTmp);
	return strTmp;
}

OS_Error Socket::Send(const char* inData, const UInt32 inLength, UInt32* outLengthSent,UInt32 vTimeOut)
{
	Assert(inData != NULL);
	
	if (!(fState & kConnected))
	return (OS_Error)ENOTCONN;

	//timout calc
	int err = 0;
	if(vTimeOut != 0)
	{
		struct timeval val;
		val.tv_sec = 0;
		val.tv_usec = vTimeOut * 1000;
		
		fd_set TmpSet;
		FD_ZERO(&TmpSet);
		FD_SET(fFileDesc,&TmpSet);
		err = select(fFileDesc+1, NULL,&TmpSet, NULL, &val);

		if(err <=0 )
		{
			if(err == 0) err = EAGAIN;
			return err;
		}
	}

	//send
	do 
	{
		err = ::send(fFileDesc, inData, inLength, 0);//flags??
	} while((err == -1) && (OS::GetErrno() == EINTR));
	
	if (err == -1)
	{
		//Are there any errors that can happen if the client is connected?
		//Yes... EAGAIN. Means the socket is now flow-controleld
		int theErr = OS::GetErrno();
		if ((theErr != EAGAIN) && (this->IsConnected()))
			fState ^= kConnected;//turn off connected state flag
		
		return (OS_Error)theErr;
	}
	
	*outLengthSent = err;
	return OS_NoErr;
}

OS_Error Socket::WriteV(const struct iovec* iov, const UInt32 numIOvecs, UInt32* outLenSent,UInt32 vTimeOut)
{
    Assert(iov != NULL);

    if (!(fState & kConnected))
        return (OS_Error)ENOTCONN;

	//timout calc
	int err = 0;
	if(vTimeOut != 0)
	{
		struct timeval val;
		val.tv_sec = 0;
		val.tv_usec = vTimeOut * 1000;
		
		fd_set TmpSet;
		FD_ZERO(&TmpSet);
		FD_SET(fFileDesc,&TmpSet);
		err = select(fFileDesc+1, NULL,&TmpSet, NULL, &val);

		if(err <=0 )
		{
			if(err == 0) err = EAGAIN;
			return err;
		}
	}

    do 
   {
#ifdef __Win32__
        DWORD theBytesSent = 0;
        err = ::WSASend(fFileDesc, (LPWSABUF)iov, numIOvecs, &theBytesSent, 0, NULL, NULL);
        if (err == 0)
            err = theBytesSent;
#else
       err = ::writev(fFileDesc, iov, numIOvecs);//flags??
#endif
    } while((err == -1) && (OS::GetErrno() == EINTR));

    if (err == -1)
    {
        // Are there any errors that can happen if the client is connected?
        // Yes... EAGAIN. Means the socket is now flow-controleld
        int theErr = OS::GetErrno();
        if ((theErr != EAGAIN) && (this->IsConnected()))
            fState ^= kConnected;//turn off connected state flag

        return (OS_Error)theErr;
    }

    *outLenSent = (UInt32)err;
    return OS_NoErr;
}

OS_Error Socket::Peek(void *buffer, const UInt32 length, UInt32 *outRecvLenP)
{
	Assert(outRecvLenP != NULL);
    Assert(buffer != NULL);
	
    if (!(fState & kConnected))
        return (OS_Error)ENOTCONN;
	
    //int theRecvLen = ::recv(fFileDesc, buffer, length, 0);//flags??
    int theRecvLen;
    do {
		theRecvLen = ::recv(fFileDesc, (char*)buffer, length, MSG_PEEK);//flags??
    } while((theRecvLen == -1) && (OS::GetErrno() == EINTR));
	
    if (theRecvLen == -1)
    {
        // Are there any errors that can happen if the client is connected?
        // Yes... EAGAIN. Means the socket is now flow-controleld
        int theErr = OS::GetErrno();
        if ((theErr != EAGAIN ) && (this->IsConnected()))
            fState ^= kConnected;//turn off connected state flag
		
        return (OS_Error)theErr;
    }
    //if we get 0 bytes back from read, that means the client has disconnected.
    //Note that and return the proper error to the caller
    else if (theRecvLen == 0)
    {
        fState ^= kConnected;
        return (OS_Error)ENOTCONN;
    }
    Assert(theRecvLen > 0);
    *outRecvLenP = (UInt32)theRecvLen;
    return OS_NoErr;
}

OS_Error Socket::Read(void *buffer, const UInt32 length, UInt32 *outRecvLenP)
{
    Assert(outRecvLenP != NULL);
    Assert(buffer != NULL);

    if (!(fState & kConnected))
        return (OS_Error)ENOTCONN;

    //int theRecvLen = ::recv(fFileDesc, buffer, length, 0);//flags??
    int theRecvLen;
    do {
       theRecvLen = ::recv(fFileDesc, (char*)buffer, length, 0);//flags??
    } while((theRecvLen == -1) && (OS::GetErrno() == EINTR));

    if (theRecvLen == -1)
    {
        // Are there any errors that can happen if the client is connected?
        // Yes... EAGAIN. Means the socket is now flow-controleld
        int theErr = OS::GetErrno();
        if ((theErr != EAGAIN ) && (this->IsConnected()))
            fState ^= kConnected;//turn off connected state flag

        return (OS_Error)theErr;
    }
    //if we get 0 bytes back from read, that means the client has disconnected.
    //Note that and return the proper error to the caller
    else if (theRecvLen == 0)
    {
        fState ^= kConnected;
        return (OS_Error)ENOTCONN;
    }

    Assert(theRecvLen > 0);
    *outRecvLenP = (UInt32)theRecvLen;

    return OS_NoErr;
}

OS_Error Socket::SetNonBlocking()
{
#ifdef __Win32__
    u_long one = 1;
    int err = ::ioctlsocket(fFileDesc, FIONBIO, &one);
#else
    int flag = ::fcntl(fFileDesc, F_GETFL, 0);
    int err = ::fcntl(fFileDesc, F_SETFL, flag | O_NONBLOCK);
#endif

	m_bIsBlocking = FALSE;

	return err;
}

OS_Error Socket::SetBlocking()
{
#ifdef __Win32__
    u_long one = 0;
    int err = ::ioctlsocket(fFileDesc, FIONBIO, &one);
#else
    int flag = ::fcntl(fFileDesc, F_GETFL, 0);
    int err = ::fcntl(fFileDesc, F_SETFL, flag & ~O_NONBLOCK);
#endif
	
	m_bIsBlocking = TRUE;

	return err;
}





